package cn.tannn.tconfig.client.value;

import cn.tannn.tconfig.client.util.FieldUtils;
import cn.tannn.tconfig.client.util.PlaceholderHelper;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.List;


/**
 * spring value注解处理器
 * <p> 1. 扫描所有的 spring value ,并保存
 * <p> 2. 在配置发生变更时更新所有的spring value
 *
 * @author <a href="https://tannn.cn/">tnnn</a>
 * @version V1.0
 * @date 2024/5/9 下午7:04
 */
@Slf4j
public class SpringValueProcessor implements BeanPostProcessor, BeanFactoryAware, ApplicationListener<EnvironmentChangeEvent> {

    static final PlaceholderHelper helper = PlaceholderHelper.getInstance();
    static final MultiValueMap<String, SpringValue> VALUE_HOLDER = new LinkedMultiValueMap<>();
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        FieldUtils.findAnnotatedField(bean.getClass(), Value.class).forEach(field -> {
            log.info("[tconfig] findAnnotatedField >> find spring value {}", field);
            Value value = field.getAnnotation(Value.class);
            helper.extractPlaceholderKeys(value.value()).forEach(key -> {
                log.info("[tconfig] helper >> find spring value {}", key);
                SpringValue springValue = new SpringValue(bean, beanName, key, value.value(), field);
                VALUE_HOLDER.add(key, springValue);
            });
        });
        return bean;
    }

    @Override
    public void onApplicationEvent(EnvironmentChangeEvent event) {
        log.info("[tconfig] >> update spring value for keys: {}", event.getKeys());
        event.getKeys().forEach(key -> {
            log.info("[tconfig] >> update spring value: {}", key);
            List<SpringValue> springValues = VALUE_HOLDER.get(key);
            if (springValues == null || springValues.isEmpty()) {
                return;
            }
            springValues.forEach(springValue -> {
                log.info("[tconfig] >> update spring value: {} for key {}", springValue, key);
                try {
                    // 拿到 @Value("${}") 的值
                    Object value = helper.resolvePropertyValue((ConfigurableBeanFactory) beanFactory
                            , springValue.getBeanName()
                            , springValue.getPlaceholder());
                    // 将@Value 表达式里的值 设置到Bean的字段上去
                    springValue.getField().setAccessible(true);
                    springValue.getField().set(springValue.getBean(), value);
                    log.info("[tconfig] >> update value: {} for holder {}", value, springValue.getPlaceholder());
                } catch (Exception ex) {
                    log.error("[tconfig] >> update spring value error", ex);
                }
            });
        });
    }


}
